package utils

import (
	"image"
	"image/color"
	"image/draw"
	"log/slog"
	"os"
	"strings"

	"github.com/disintegration/imaging"
	"github.com/nfnt/resize"
)

type CircleImage struct {
	p image.Point
	r int
}

func (c *CircleImage) ColorModel() color.Model {
	return color.AlphaModel
}

func (c *CircleImage) Bounds() image.Rectangle {
	return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
}

func (c *CircleImage) At(x, y int) color.Color {
	xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
	if xx*xx+yy*yy < rr*rr {
		return color.Alpha{A: 255}
	}
	return color.Alpha{A: 0}
}

func RoundImage(img image.Image) image.Image {
	var bounds = img.Bounds()
	var radius = bounds.Dx() / 2
	if bounds.Dx() > bounds.Dy() {
		radius = bounds.Dy() / 2
	}

	var centerX = bounds.Dx() / 2
	var centerY = bounds.Dy() / 2

	dest := image.NewNRGBA(image.Rect(0, 0, radius*2, radius*2))
	draw.DrawMask(dest, dest.Bounds(), img, image.Point{},
		&CircleImage{image.Point{X: centerX, Y: centerY}, radius}, image.Point{}, draw.Over)
	return dest
}

func GenThumb(imagePath string, maxWidth uint, maxHeight uint) (string, error) {
	// decode into image.Image
	img, err := imaging.Open(imagePath)
	if err != nil {
		return "", err
	}

	dstImage := resize.Thumbnail(maxWidth, maxHeight, img, resize.Lanczos3)

	thumbPath := imagePath[0:strings.LastIndex(imagePath, ".")] + "_thumb.jpg"
	if err := imaging.Save(dstImage, thumbPath, imaging.JPEGQuality(100)); err != nil {
		return "", err
	}
	return thumbPath, nil
}

func ResetByThumb(imagePath string, maxWidth uint, maxHeight uint) error {
	// decode into image.Image
	img, err := imaging.Open(imagePath)
	if err != nil {
		return err
	}

	// 大小在max之内，直接返回
	if uint(img.Bounds().Dx()) <= maxWidth && uint(img.Bounds().Dy()) <= maxHeight {
		return nil
	}

	dstImage := resize.Thumbnail(maxWidth, maxHeight, img, resize.Lanczos3)

	if err := imaging.Save(dstImage, imagePath); err != nil {
		return err
	}
	return nil
}

func ResetByJPEG(imagePath string, maxWidth uint, maxHeight uint, maxSize int64) (string, error) {
	// decode into image.Image
	img, err := imaging.Open(imagePath)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}

	fileInfo, err := os.Stat(imagePath)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}

	// 大小在max之内，直接返回
	if uint(img.Bounds().Dx()) <= maxWidth && uint(img.Bounds().Dy()) <= maxHeight &&
		fileInfo.Size() <= maxSize {
		return imagePath, nil
	}

	dstImage := resize.Thumbnail(maxWidth, maxHeight, img, resize.Lanczos3)

	var dstPath = imagePath
	if strings.LastIndex(imagePath, ".png") == len(imagePath)-4 ||
		strings.LastIndex(imagePath, ".PNG") == len(imagePath)-4 {
		dstPath = imagePath[0:len(imagePath)-4] + ".jpg"
	}

	if err := imaging.Save(dstImage, dstPath); err != nil {
		slog.Error(err.Error())
		return "", err
	}

	fileInfo, err = os.Stat(dstPath)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}
	slog.Debug("dest image info", "path", dstPath, "size", fileInfo.Size())

	var quality = 90
	for err == nil && fileInfo.Size() > maxSize && quality > 0 {
		// 如果大小超过指定最大大小，进行压缩
		if err := imaging.Save(dstImage, dstPath, imaging.JPEGQuality(quality)); err != nil {
			slog.Error(err.Error(), "path", dstPath)
		}
		quality = quality - 10

		// 再次读取文件大小
		fileInfo, err = os.Stat(dstPath)
		if err != nil {
			slog.Error(err.Error())
		}
	}

	return dstPath, nil
}

func ResetImageSquare(imagePath string, width int) {
	f, _ := os.Open(imagePath)
	srcImage, _, _ := image.Decode(f)
	dstImage := imaging.Fill(srcImage, width, width, imaging.Center, imaging.Lanczos)
	err := imaging.Save(dstImage, imagePath, imaging.JPEGQuality(100))
	if err != nil {
		slog.Error(err.Error(), "imagePath", imagePath, "width", width)
		return
	}
}

func PasteToBgCenter(bgPath string, srcPath string, rounded bool, dstPath string) error {
	bgFile, err := os.Open(bgPath)
	if err != nil {
		return err
	}
	bgImage, _, err := image.Decode(bgFile)
	if err != nil {
		return err
	}

	srcFile, err := os.Open(srcPath)
	if err != nil {
		return err
	}
	srcImage, _, err := image.Decode(srcFile)
	if err != nil {
		return err
	}

	if !rounded {
		dstImage := imaging.PasteCenter(bgImage, srcImage)
		err := imaging.Save(dstImage, dstPath)
		if err != nil {
			slog.Error(err.Error())
			return err
		}
	} else {

		var srcBounds = srcImage.Bounds()
		var radius = srcBounds.Dx() / 2
		if srcBounds.Dx() > srcBounds.Dy() {
			radius = srcBounds.Dy() / 2
		}

		var bgBounds = bgImage.Bounds()
		var centerX = bgBounds.Dx() / 2
		var centerY = bgBounds.Dy() / 2

		dstImage := image.NewRGBA(bgImage.Bounds())
		draw.Draw(dstImage, bgImage.Bounds(), bgImage, image.Point{}, draw.Src)
		draw.DrawMask(dstImage, image.Rect(centerX-radius, centerY-radius, centerX+radius, centerY+radius),
			srcImage, image.Point{}, &CircleImage{image.Point{X: centerX, Y: centerY}, radius},
			image.Point{X: centerX - radius, Y: centerY - radius}, draw.Over)

		err := imaging.Save(dstImage, dstPath)
		if err != nil {
			slog.Error(err.Error())
			return err
		}
	}
	return nil
}
